#include "pipelinestack.h"
#include <QTimer>

#include <qeventloop.h>

#include "componentmanager.h"
#include "wcs/WCSSend.h"
#include "stackplate/stackplatemanager.h"
#include "boxline/BoxlineManager.h"

#include "../HttpServer/httpsend.h"

#include "pipeline_log.h"

#include "fuzhou_data.h"

inline void TimeDelay(int s)
{
	QEventLoop loop;
	QTimer::singleShot(s * 1000, &loop, &QEventLoop::quit);
	loop.exec();
}

PipelineStack::PipelineStack()
{}

PipelineStack::~PipelineStack()
{}

bool PipelineStack::Init()
{
	if (m_info.type != PipelineType::pipeline_stack) {
		log_error("pipeline Type error, this type = 1, but input type is {}", (int)m_info.type);
		SetPipelineErrorState("Bug");
		return false;
	}
	// 获取wcs 发送实例
	m_wcs = dynamic_cast<WCSSend*>(component->GetComponent(Component::component_wcsSend));
	if (m_wcs == nullptr) {
		log_error("pipeline {} init failed, get wcs failed", m_info.id);
		return false;
	}

	// 获取码垛托盘管理实例
	auto platm = dynamic_cast<StackPlateManager*>(component->GetComponent(Component::component_stackPlate));
	if (platm == nullptr) {
		log_error("pipeline {} init failed, get stack plate  Manager failed", m_info.id);
		return false;
	}
	m_plate = platm->GetPlate(m_info.putdown);
	if (m_plate == nullptr) {
		SetPipelineErrorState(QString("未匹配到放置对应的托盘编号：%1").arg(m_info.putdown));
		return false;
	}
	// 获取输送线指针
	auto bolineM = dynamic_cast<BoxlineManager*>(component->GetComponent(Component::component_boxline));
	if (bolineM == nullptr) {
		log_error("pipeline {} init failed, get boxline mananger failed", m_info.id);
		SetPipelineErrorState(QString("%1 获取输送线组件失败, 无法加载").arg(m_info.id));
		return false;
	}
	m_line = bolineM->GetBoxlineByID(m_info.grab);
	if (m_line == nullptr) {
		log_error("pipeline {} init failed, get boxline failed");
		SetErrorMassage(QString("%1 获取输送线实例失败, 无法加载").arg(m_info.id));
		return false;
	}

	return true;
}
int PipelineStack::IfTaskAvailable(const QByteArray& task)
{
	TaskDataJ data = {};
	data.fromJson(task);
	if (!CheckTaskData(data)) {
		return -1;
	}
	if (m_task.isBusy) {
		SetTraceMassage(QString("当前正在执行任务 %1，无法接收新任务（%2）").arg(m_task.taskId).arg(data.taskId()));
		log_error("Task matching, but there are unfinished tasks");
		return 1;
	}
	m_task.stackType = data.stackType();
	m_task.taskId = data.taskId();
	m_task.taskNum = data.taskNum();
	auto& sku = data.skuList().at(0);
	m_sku.skuNo = sku.skuNo();
	m_sku.pos = 0;
	m_sku.size.width = sku.skuWidth();
	m_sku.size.height = sku.skuHeight();
	m_sku.size.length = sku.skuLength();
	m_sku.type = sku.boxType();
	m_sku.weight = sku.skuWeight();
	m_sku.valied = true;
	m_task.isBusy = true;
	m_sku.valied = true;
	SetTraceMassage(QString("收到新任务 %1, 任务数量：%2").arg(m_task.taskId).arg(m_task.taskNum));
	return 0;
}
/*生成准备节拍*/
bool PipelineStack::InitPrepareStep(int id)
{
	if (m_prepareStep.valied) {
		log_error("准备节拍数据不为空，无法生成新数据");
		SetPipelineErrorState(QString("准备节拍数据不为空，无法生成新数据, bug"));
		return false;
	}
	// 准备节拍数据生成
	m_prepareStep.id = id;
	m_stepId = id;
	// 节拍执行数量先按照最大执行数量设置
	m_prepareStep.excuteNum = m_task.taskNum - m_exec.finishNum - m_exec.execNum;

	// 计算当前抓取数量
	int num = CalculateNextGrab();
	if (num < 0) {
		log_error("CalculateNextGrab num = {} failed", num);
		m_prepareStep.excuteNum = 0;
		m_prepareStep.valied = false;
		return false;
	} else if (num > 0) {
		// 申请物料
		if (!ApplySku(num)) {
			log_error("sku apply failed, num = {}", num);
			SetPipelineErrorState(QString("向wcs申请物料%1失败").arg(num));
			return false;
		}
		m_prepareStep.excuteNum = num;
		m_prepareStep.valied = true;
	} else {// 说明任务已经准备完成，不要生成新节拍
		m_prepareStep.excuteNum = 0;
		m_prepareStep.valied = false;
		return true;
	}
	SetTraceMassage(QString("节拍%1初始化成功").arg(m_prepareStep.id));
	return true;
}


bool PipelineStack::ApplySku(int num)
{
	// 申请前先设置物料信息
	if (!m_line->UpdateSkuInfo(m_sku)) {
		SetPipelineErrorState(QString("抓取位置[%1]更新物料信息失败").arg(m_info.grab));
		return false;
	}

	// 申请物料
	wcs::RobItemApply apply = {};
	apply.num = num;
	apply.pos = 0;
	apply.robNo = m_info.rob;
	apply.skuNo = m_sku.skuNo;
	apply.taskId = m_task.taskId;
	apply.msgId = m_exec.finishNum + m_exec.execNum + 1;
	apply.type = 1; // 1 物料, 2 拆垛托盘、3 码垛托盘、4 托盘垛。
	wcs::ItemApplyAns ans = {};
	QString err;
	bool ret = m_wcs->SendRequestInfo(apply, err);
	if (!ret) {
		SetPipelineErrorState(QString("向wcs申请物料失败,原因：%1").arg(err));
		log_error("wcs refuse current apply");
		return false;
	} else {
		log_error("Apply approved, result = {}, num = {}", ans.result, ans.num);
		SetTraceMassage(QString("申请%1个物料成功").arg(num));
		return true;
	}
}

/*计算下一次的抓取数量*/
int PipelineStack::CalculateNextGrab()
{
	if (m_prepareStep.excuteNum <= 0) {
		return 0;
	}
	QString err;
	bool ret = m_plate->CalculateNextGrab(m_prepareStep.id, m_prepareStep.excuteNum, err);
	if (!ret) {
		SetPipelineErrorState(QString("%1算法计算失败，原因：%2").arg(m_info.putdown).arg(err));
		return -1;
	}
	int num = m_plate->GetNextGrabNum(m_prepareStep.id);

	if (num <= 0) {
		SetPipelineErrorState(QString("%1获取计划抓取数量异常，value：%2").arg(m_info.putdown).arg(num));
		return -1;
	}

	return num;
}


bool PipelineStack::TaskInit()
{
	// 初始化执行信息
	m_exec.clear();
	m_prepareStep.clear();
	if (!m_plate->UpdataPlateSkuInfo(m_sku, m_task.stackType)) {
		SetPipelineErrorState(QString("放货位置[%1]更新物料信息失败").arg(m_info.putdown));
		return false;
	}
	// 托盘位置初始化物料	
	int num = m_plate->GetPlateSkuNum();
	if (num > 0) {
		m_exec.finishNum = m_plate->GetPlateSkuNum();
		SetTraceMassage(QString("任务已完成数量为%1").arg(m_exec.finishNum));
	} 

	// 任务下发，表示码垛托盘已经到位，所以设置托盘到位
	if (!m_plate->SetPlateArrived()) {
		SetPipelineErrorState(QString("放货位置[%1]设置托盘到位失败").arg(m_info.putdown));
		return false;
	}

	return InitPrepareStep(1);
}
// 任务是否开始
bool PipelineStack::IfTaskStarted()
{
	// 判断码垛托盘是否到位
	if (m_plate->GetPlateState()) {
		return true;
	}
	SetPipelineErrorState("码垛托盘未到位，bug");
	return false;
}
/*任务是否准备完成*/
bool PipelineStack::IfTaskPrepared()
{
	log_trace("task ={}, finish ={},m_exec = {}",
		m_task.taskNum, m_exec.finishNum, m_exec.execNum);
	int redNum = m_task.taskNum - m_exec.finishNum - m_exec.execNum;
	if (redNum <= 0) {
		m_prepareStep.clear();
		return true;
	} else {
		return false;
	}
}


int PipelineStack::StepPrepare()
{
	if (!m_prepareStep.valied) {	// 准备节拍数据未生成，等待
		return 1;
	}

	// 判断物料是否到位
	if (!m_line->GetState()) {
		return 1;	// 物料没有到位
	}

	int num = m_line->GetSkuNum();
	if (num > m_prepareStep.excuteNum || num <= 0) {
		SetPipelineErrorState("输送线到位数量异常，节拍执行出错");
		log_error("{} line arrive num ={}, plan num = {}",
			m_info.grab, num, m_prepareStep.excuteNum);
		return -1;
	} else if (num < m_prepareStep.excuteNum) {	// 到位数量小于计划数量，需要重新计算位置点
		m_prepareStep.excuteNum = num;
		int num = CalculateNextGrab();	// 按照输送线到位的物料数量重新计算
		if (num != m_prepareStep.excuteNum) {
			SetPipelineErrorState("码垛托盘重新计算结果与输送到位数量不匹配");
			return -1;
		}
	}
	/*获取抓取点放置点坐标*/
	QString err;
	if (!m_line->CalculateNextGrab(m_prepareStep.id, m_prepareStep.excuteNum, err)) {
		SetPipelineErrorState(QString("抓取点[%1]计算抓取坐标失败：%2").arg(m_info.grab).arg(err));
		return -1;
	}
	if (!m_line->GetSkuPosition(m_prepareStep.id, m_prepareStep.grabPos)) {
		SetPipelineErrorState(QString("获取抓取点[%1]坐标失败").arg(m_info.grab));
		return -1;
	}
	if (!m_plate->GetNextGrabPosition(m_prepareStep.id, m_prepareStep.putPos)) {
		SetPipelineErrorState(QString("获取放置点[%1]坐标失败").arg(m_info.putdown));
		return -1;
	}
	/*获取执行路径*/
	AddPathListToPrepareStepInfo(m_info.paths.at(0));

	SetTraceMassage(QString("节拍%1准备完成").arg(m_prepareStep.id));
	return 0;
}


json_struct(MoveInfoJ,
	mapping(QString, robNo);
mapping(QString, taskId);
mapping(QString, boxLine);
mapping(int, boxNum);
prop(&robNo, &taskId, &boxLine, &boxNum);
);


bool PipelineStack::TaskFinish()
{
	SetTraceMassage(QString("任务执行完成，发送任务完成通知"));
	wcs::RobTaskFinish data = {};
	data.robNo = m_info.rob;
	data.taskId = m_task.taskId;
	data.residueNum = 0;
	data.stackFinish = 1;
	data.taskFinish = 1;
	data.stackPlace = m_info.putdown;
	data.taskNum = m_task.taskNum;
	QString err;
	if (!m_wcs->SendTaskFinishInfo(data, err)) {
		log_error("send Task Finish info failed, err = {}", err);
		SetErrorMassage(QString("发送任务完成通知失败，%1").arg(err));
		return false;
	}

	if (!m_plate->SetPlateMoved()) {
		log_trace("{} plate {} move failed");
		SetErrorMassage("清理托盘信息出错");
		return false;
	}
	DataInit();
	return true;
}

bool PipelineStack::CheckTaskData(const TaskDataJ& data)
{
	if (data.taskType() != 2 || data.robNo() != m_info.rob) {
		return false;
	}
	if (data.skuList().size() != 1) {
		return false;
	}
	if (m_info.putdown != data.stackPlace() || m_info.grab != data.skuList().at(0).boxLine()) {
		return false;
	}
	auto& sku = data.skuList().at(0);
	if (sku.skuHeight() <= 0 || sku.skuLength() <= 0 || sku.skuWidth() <= 0) {
		log_error("Sku Size error");
		return false;
	}

	log_trace("{} reciever a task id = {}", m_info.id, data.taskId());
	return true;
}



/*路径执行前操作*/
bool PipelineStack::PathStart(const QString& path)
{
	// 放货路径执行前，清空输送线
	auto paths = m_info.paths.at(0).split(',');
	if (path == paths.at(1)) {
		m_line->SetSkuMoved();
	}
	if (path == paths.at(0)) {
		// 放货路径完成后，将货物数量新增到码垛托盘
		return m_line->SetSkuMoved();
	}

	return true;
}
bool PipelineStack::SendNotifyer(int type)
{
	if (type == 1) {	// 发送物料申请
		InitPrepareStep(++m_stepId);
		return true;
	}
	log_error("Send notifiy, type = {}", type);
	return false;
}
/*路径执行后特殊操作*/
bool PipelineStack::PathEnd(const QString& path)
{
	auto paths = m_info.paths.at(0).split(',');
	if (path == paths.at(1)) {
		// 放货路径完成后，将货物数量新增到码垛托盘
		return m_plate->AddSkuToPlate(m_waitFinishStep.excuteNum);
	}
	return true;
}

